#============================================================
#	Context Steering Move
#============================================================
# @datetime: 2022-8-3 14:33:18
#============================================================

##  上下文移动
##[br] 根据周围是否有碰撞对象来进行操控移动方向
class_name ContextSteeringMove
extends Node2D


## 移动 (velocity：移动向量)
signal moved(velocity)


## 移动速度
@export var move_speed = 100
## 上个移动向量转为当前的移动向量转换速度
@export_range(0.0, 1.0) 
var steer_force = 0.1
## 射线检测距离
@export 
var look_ahead = 100
## 用于检测的射线的个数
@export 
var num_rays = 8
## 射线所检测的层（默认检测第 0 层的位置）
@export_flags_2d_physics
var ray_collision_layer : int = (1 << 0)


## 几个射线方向
var ray_directions = []
## 意向值
var interest = []
## 危险程度
var danger = []


## 最终选择移动的方向
var chosen_dir = Vector2.ZERO
## 移动向量
var velocity = Vector2.ZERO
## 射线排除检测对象
var exclude_object : Array = []


#============================================================
#   内置
#============================================================
func _ready():
	# 设置数组大小
	interest.resize(num_rays)
	danger.resize(num_rays)
	ray_directions.resize(num_rays)
	# 每个射线的方向
	for i in num_rays:
		var angle = i * 2 * PI / num_rays
		ray_directions[i] = Vector2.UP.rotated(angle)


#============================================================
#   自定义
#============================================================
## 添加射线排除检测的对象
func add_exclude_object(object: CollisionObject2D):
	exclude_object.append(object)


##  移动方向
##[br]
##[br][code]dir[/code]  
func move_to_dir(dir: Vector2):
	_interest(dir)
	_danger()
	_choose_direction()
	
	# 选择移动到的方向偏移旋转的位置得到最终的移动向量
	var desired_velocity = chosen_dir.rotated(global_rotation) * move_speed
	if desired_velocity != Vector2.ZERO:
		# 移动向量
		velocity = velocity.linear_interpolate(desired_velocity, steer_force)
	else:
		# 如果为 Vector2(0,0)，则周围都存在障碍物
		velocity = Vector2.UP.rotated(global_rotation + randf_range(-PI, PI)) * move_speed * randf()
	
	emit_signal("moved", velocity)


##  移动
##[br]
##[br][code]pos[/code]  移动到的位置
func move_to_pos(pos: Vector2):
	move_to_dir(global_position.direction_to(pos))


##  移动意愿 
##[br]
##[br][code]move_dir[/code]  要移动的方向
func _interest(move_dir: Vector2):
	# 根据当前的位置获取下一个位置的移动方向
	for i in num_rays:
		var d = ray_directions[i].rotated(global_rotation).dot(move_dir)
		interest[i] = max(0, d)


# 碰撞风险
func _danger():
	var space_state := get_world_2d().direct_space_state as PhysicsDirectSpaceState2D
	for i in num_rays:
		# 检测每个射线方向是否有碰撞
		var param := PhysicsRayQueryParameters2D.create(
			global_position, 
			global_position + ray_directions[i].rotated(global_rotation) * look_ahead,
			ray_collision_layer,
			exclude_object
		)
		param.collide_with_areas = false
		param.collide_with_bodies = true
		var result = space_state.intersect_ray(param)
		# 如果有碰撞，则设置风险值权重为 1
		danger[i] = 1.0 if result else 0


# 根据移动意向和碰撞风险之间计算最终移动方向
func _choose_direction():
	for i in num_rays:
		# 如果有危险情况，则意向改为0
		if danger[i] > 0.0:
			interest[i] = 0.0
	# 根据每个方向的移动意向，计算移动的方向
	chosen_dir = Vector2.ZERO
	for i in num_rays:
		# 有危险则不加上这个方向，无危险，则加上这个方向
		chosen_dir += ray_directions[i] * interest[i]
	# 归一化
	chosen_dir = chosen_dir.normalized()



